/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"
#include "NV_DefineSpace_fp.h"

#if CC_NV_DefineSpace  // Conditional expansion of this file
/*
This command defines the attributes of an NV Index and causes the TPM to reserve space to hold the data associated with the NV Index. If a definition already exists at the NV Index, the TPM will returnTPM_RC_NV_DEFINED.
The TPM will return TPM_RC_ATTRIBUTES if nvIndexType has a reserved value in publicInfo.
NOTE 1 It is not required that any of these three attributes be set.
The TPM shall return TPM_RC_ATTRIBUTES if TPMA_NV_WRITTEN, TPMA_NV_READLOCKED, or TPMA_NV_WRITELOCKED is SET.
If nvIndexType is TPM_NT_COUNTER, TPM_NT_BITS, TPM_NT_PIN_FAIL, or TPM_NT_PIN_PASS, then publicInfo→dataSize shall be set to eight (8) or the TPM shall return TPM_RC_SIZE.
If nvIndexType is TPM_NT_EXTEND, then publicInfo→dataSize shall match the digest size of the publicInfo.nameAlg or the TPM shall return TPM_RC_SIZE.
NOTE 2 TPM_RC_ATTRIBUTES could be returned by a TPM that is based on the reference code of older versions of the specification but the correct response for this error is TPM_RC_SIZE.
If the NV Index is an ordinary Index and publicInfo→dataSize is larger than supported by the TPM implementation then the TPM shall return TPM_RC_SIZE.
NOTE 3 The limit for the data size may vary according to the type of the index. For example, if the index has TPMA_NV_ORDERLY SET, then the maximum size of an ordinary NV Index may be less than the size of an ordinary NV Index that has TPMA_NV_ORDERLY CLEAR.
At least one of TPMA_NV_PPREAD, TPMA_NV_OWNERREAD, TPMA_NV_AUTHREAD, or TPMA_NV_POLICYREAD shall be SET or the TPM shall return TPM_RC_ATTRIBUTES.
At least one of TPMA_NV_PPWRITE, TPMA_NV_OWNERWRITE, TPMA_NV_AUTHWRITE, or TPMA_NV_POLICYWRITE shall be SET or the TPM shall return TPM_RC_ATTRIBUTES.
If TPMA_NV_CLEAR_STCLEAR is SET, then nvIndexType shall not be TPM_NT_COUNTER or the TPM shall return TPM_RC_ATTRIBUTES.
If platformAuth/platformPolicy is used for authorization, then TPMA_NV_PLATFORMCREATE shall be SET in publicInfo. 
If ownerAuth/ownerPolicy is used for authorization, TPMA_NV_PLATFORMCREATE shall be CLEAR in publicInfo. 
If TPMA_NV_PLATFORMCREATE is not set correctly for the authorization, the TPM shall return TPM_RC_ATTRIBUTES.
If TPMA_NV_POLICY_DELETE is SET, then the authorization shall be with Platform Authorization or the TPM shall return TPM_RC_ATTRIBUTES.
If nvIndexType is TPM_NT_PIN_FAIL, then TPMA_NV_NO_DA shall be SET. Otherwise, the TPM shall return TPM_RC_ATTRIBUTES.
NOTE 4 The intent of a PIN Fail index is that its DA protection is on a per-index basis, not based on the global DA protection. This avoids conflict over which type of dictionary attack protection is in use.
If nvIndexType is TPM_NT_PIN_FAIL or TPM_NT_PIN_PASS, then at least one of TPMA_NV_PPWRITE, TPMA_NV_OWNERWRITE, or TPMA_NV_POLICYWRITE shall be SET or the TPM shall return TPM_RC_ATTRIBUTES. TPMA_NV_AUTHWRITE shall be CLEAR. Otherwise, the TPM shall return TPM_RC_ATTRIBUTES.
NOTE 5 If TPMA_NV_AUTHWRITE was SET for a PIN Pass index, a user knowing the authorization value could decrease pinCount or increase pinLimit, defeating the purpose of a PIN Pass index. 
The requirement is also enforced for a PIN Fail index for consistency

此命令定义NV索引的属性，并使TPM保留空间以保存与NV索引相关联的数据。如果NV索引中已存在定义，TPM将返回TPM_RC_NV_DEFINED。
如果nvIndexType在publicInfo中具有保留值，TPM将返回TPM_RC_ATTRIBUTES。
注1：不需要设置这三个属性中的任何一个。
如果设置了TPMA_NV_WRITEN、TPMA_NG_READLOCKED或TPMA_NV-WRITELOCKED，TPM应返回TPM_RC_ATTRIBUTES。
如果nvIndexType为TPM_NT_COUNTER、TPM_NT_BITS、TPM_NT_PIN_FAIL或TPM_NT_PIN_PASS，则publicInfo→dataSize应设置为八（8），或者TPM应返回TPM_RC_SIZE。
如果nvIndexType为TPM_NT_EXTEND，则publicInfo→dataSize应与publicInfo.nameAlg的摘要大小匹配，或者TPM应返回TPM_RC_size。
注2 TPM_RC_ATTRIBUTES可能由基于旧版本规范参考代码的TPM返回，但此错误的正确响应为TPM_RC_SIZE。
如果NV指数是普通指数和publicInfo→dataSize大于TPM实现所支持的，则TPM应返回TPM_RC_SIZE。
注3数据大小的限制可能因索引类型而异。例如，如果该索引具有TPMA_NV_ORDERLY SET，则普通NV索引的最大大小可以小于具有TPMA_NV_ORDELLY CLEAR的普通NV指数的大小。
应设置TPMA_NV_PPREAD、TPMA_NV-OWNERREAD、TPPA_NV_AUTHREAD或TPPA_NV_POLICYREAD中的至少一个，或者TPM应返回TPM_RC_ATTRIBUTES。
应至少设置TPMA_NV_PPWRITE、TPMA_NV-OWNERRITE、TPPA_NV_AUTHWRITE或TPPA_NV_POLICYWRITE中的一个，或者TPM应返回TPM_RC_ATTRIBUTES。
如果设置了TPMA_NV_CLEAR_STCLEAR，则nvIndexType不应为TPM_NT_COUNTER，或者TPM应返回TPM_RC_ATTRIBUTES。
如果使用platformAuth/platformPolicy进行授权，则需要在publicInfo中设置TPMA_NV_PLATFORMCREATE。
如果使用ownerAuth/ownerPolicy进行授权，则公共信息中的TPMA_NV_PLATFORMCREATE应为CLEAR。
如果授权未正确设置TPMA_NV_PLATFORMCREATE，则TPM应返回TPM_RC_ATTRIBUTES。
如果设置了TPMA_NV_POLICY_DELTE，则授权应为平台授权，或者TPM应返回TPM_RC_ATTRIBUTES。
如果nvIndexType为TPM_NT_PIN_FAIL，则应设置TPMA_NV_NO_DA。否则，TPM应返回TPM_RC_ATTRIBUTES。
注4 PIN失败索引的目的是其DA保护基于每个索引，而不是基于全局DA保护。这避免了在使用哪种类型的字典攻击保护时发生冲突。
如果nvIndexType为TPM_NT_PIN_FAIL或TPM_NT-PIN_PASS，则应设置TPMA_NV_PPWRITE、TPMA_NV-OWNEWRITE或TPMA_NG_POLICYWRITE中的至少一个，或者TPM应返回TPM_RC_ATTRIBUTES。TPMA_NV_AUTHWRITE应为CLEAR（清除）。否则，TPM应返回TPM_RC_ATTRIBUTES。
注5：如果为PIN通行证索引设置了TPMA_NV_AUTHWRITE，则知道授权值的用户可能会减少pinCount或增加pinLimit，从而破坏PIN通行证指数的目的。
为了保持一致性，还强制执行了对PIN失败索引的要求

If the implementation does not support TPM2_NV_Increment(), the TPM shall return
TPM_RC_ATTRIBUTES if nvIndexType is TPM_NT_COUNTER.
If the implementation does not support TPM2_NV_SetBits(), the TPM shall return
TPM_RC_ATTRIBUTES if nvIndexType is TPM_NT_BITS.
If the implementation does not support TPM2_NV_Extend(), the TPM shall return
TPM_RC_ATTRIBUTES if nvIndexType is TPM_NT_EXTEND.
If the implementation does not support TPM2_NV_UndefineSpaceSpecial(), the TPM shall return
TPM_RC_ATTRIBUTES if TPMA_NV_POLICY_DELETE is SET.
After the successful completion of this command, the NV Index exists but TPMA_NV_WRITTEN will be
CLEAR. Any access of the NV data will return TPM_RC_NV_UNINITIALIZED.
In some implementations, an NV Index with the TPM_NT_COUNTER attribute may require special TPM
resources that provide higher endurance than regular NV. For those implementations, if this command
fails because of lack of resources, the TPM will return TPM_RC_NV_SPACE.
The value of auth is saved in the created structure. The size of auth is limited to be no larger than the size
of the digest produced by the NV Index's nameAlg (TPM_RC_SIZE).


*/
/*(See part 3 specification)
// Define a NV index space
*/
//  Return Type: TPM_RC
//      TPM_RC_HIERARCHY            for authorizations using TPM_RH_PLATFORM
//                                  phEnable_NV is clear preventing access to NV
//                                  data in the platform hierarchy.
//      TPM_RC_ATTRIBUTES           attributes of the index are not consistent
//      TPM_RC_NV_DEFINED           index already exists
//      TPM_RC_NV_SPACE             insufficient space for the index
//      TPM_RC_SIZE                 'auth->size' or 'publicInfo->authPolicy.size' is
//                                  larger than the digest size of
//                                  'publicInfo->nameAlg'; or 'publicInfo->dataSize'
//                                  is not consistent with 'publicInfo->attributes'
//                                  (this includes the case when the index is
//                                   larger than a MAX_NV_BUFFER_SIZE but the
//                                   TPMA_NV_WRITEALL attribute is SET)
TPM_RC
TPM2_NV_DefineSpace(
    NV_DefineSpace_In   *in             // IN: input parameter list
    )
{
    TPMA_NV         attributes = in->publicInfo.nvPublic.attributes;
    UINT16          nameSize;

    nameSize = CryptHashGetDigestSize(in->publicInfo.nvPublic.nameAlg);

// Input Validation

    // Checks not specific to type

    // If the UndefineSpaceSpecial command is not implemented, then can't have
    // an index that can only be deleted with policy
#if CC_NV_UndefineSpaceSpecial == NO
    if(IS_ATTRIBUTE(attributes, TPMA_NV, POLICY_DELETE))
        return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
#endif

    // check that the authPolicy consistent with hash algorithm

    if(in->publicInfo.nvPublic.authPolicy.t.size != 0
       && in->publicInfo.nvPublic.authPolicy.t.size != nameSize)
        return TPM_RCS_SIZE + RC_NV_DefineSpace_publicInfo;

   // make sure that the authValue is not too large
    if(MemoryRemoveTrailingZeros(&in->auth)
       > CryptHashGetDigestSize(in->publicInfo.nvPublic.nameAlg))
        return TPM_RCS_SIZE + RC_NV_DefineSpace_auth;

    // If an index is being created by the owner and shEnable is
    // clear, then we would not reach this point because ownerAuth
    // can't be given when shEnable is CLEAR. However, if phEnable
    // is SET but phEnableNV is CLEAR, we have to check here
    if(in->authHandle == TPM_RH_PLATFORM && gc.phEnableNV == CLEAR)
        return TPM_RCS_HIERARCHY + RC_NV_DefineSpace_authHandle;

    // Attribute checks
    // Eliminate the unsupported types
    switch(GET_TPM_NT(attributes))
    {
#if CC_NV_Increment == YES
        case TPM_NT_COUNTER:
#endif
#if CC_NV_SetBits == YES
        case TPM_NT_BITS:
#endif
#if CC_NV_Extend == YES
        case TPM_NT_EXTEND:
#endif
#if CC_PolicySecret == YES && defined TPM_NT_PIN_PASS
        case TPM_NT_PIN_PASS:
        case TPM_NT_PIN_FAIL:
#endif
        case TPM_NT_ORDINARY:
            break;
        default:
            return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
            break;
    }
    // Check that the sizes are OK based on the type
    switch(GET_TPM_NT(attributes))
    {
        case TPM_NT_ORDINARY:
            // Can't exceed the allowed size for the implementation
            if(in->publicInfo.nvPublic.dataSize > MAX_NV_INDEX_SIZE)
                return TPM_RCS_SIZE + RC_NV_DefineSpace_publicInfo;
            break;
        case TPM_NT_EXTEND:
            if(in->publicInfo.nvPublic.dataSize != nameSize)
                return TPM_RCS_SIZE + RC_NV_DefineSpace_publicInfo;
            break;
        default:
            // Everything else needs a size of 8
            if(in->publicInfo.nvPublic.dataSize != 8)
                return TPM_RCS_SIZE + RC_NV_DefineSpace_publicInfo;
            break;
    }
    // Handle other specifics
    switch(GET_TPM_NT(attributes))
    {
        case TPM_NT_COUNTER:
            // Counter can't have TPMA_NV_CLEAR_STCLEAR SET (don't clear counters)
            if(IS_ATTRIBUTE(attributes, TPMA_NV, CLEAR_STCLEAR))
                return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
            break;
#ifdef TPM_NT_PIN_FAIL
        case TPM_NT_PIN_FAIL:
            // NV_NO_DA must be SET and AUTHWRITE must be CLEAR
            // NOTE: As with a PIN_PASS index, the authValue of the index is not
            // available until the index is written. If AUTHWRITE is the only way to
            // write then index, it could never be written. Rather than go through
            // all of the other possible ways to write the Index, it is simply
            // prohibited to write the index with the authValue. Other checks
            // below will insure that there seems to be a way to write the index
            // (i.e., with platform authorization , owner authorization,
            // or with policyAuth.)
            // It is not allowed to create a PIN Index that can't be modified.
            if(!IS_ATTRIBUTE(attributes, TPMA_NV, NO_DA))
                return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
#endif
#ifdef TPM_NT_PIN_PASS
        case TPM_NT_PIN_PASS:
            // AUTHWRITE must be CLEAR (see note above to TPM_NT_PIN_FAIL)
            if(IS_ATTRIBUTE(attributes, TPMA_NV, AUTHWRITE)
               || IS_ATTRIBUTE(attributes, TPMA_NV, GLOBALLOCK)
               || IS_ATTRIBUTE(attributes, TPMA_NV, WRITEDEFINE))
                return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
#endif  // this comes before break because PIN_FAIL falls through
            break;
        default:
            break;
    }

    // Locks may not be SET and written cannot be SET
    if(IS_ATTRIBUTE(attributes, TPMA_NV, WRITTEN)
       || IS_ATTRIBUTE(attributes, TPMA_NV, WRITELOCKED)
       || IS_ATTRIBUTE(attributes, TPMA_NV, READLOCKED))
        return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

    // There must be a way to read the index.
    if(!IS_ATTRIBUTE(attributes, TPMA_NV, OWNERREAD)
       && !IS_ATTRIBUTE(attributes, TPMA_NV, PPREAD)
       && !IS_ATTRIBUTE(attributes, TPMA_NV, AUTHREAD)
       && !IS_ATTRIBUTE(attributes, TPMA_NV, POLICYREAD))
        return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

    // There must be a way to write the index
    if(!IS_ATTRIBUTE(attributes, TPMA_NV, OWNERWRITE)
       && !IS_ATTRIBUTE(attributes, TPMA_NV, PPWRITE)
       && !IS_ATTRIBUTE(attributes, TPMA_NV, AUTHWRITE)
       && !IS_ATTRIBUTE(attributes, TPMA_NV, POLICYWRITE))
        return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

    // An index with TPMA_NV_CLEAR_STCLEAR can't have TPMA_NV_WRITEDEFINE SET
    if(IS_ATTRIBUTE(attributes, TPMA_NV, CLEAR_STCLEAR)
       &&  IS_ATTRIBUTE(attributes, TPMA_NV, WRITEDEFINE))
        return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

    // Make sure that the creator of the index can delete the index
    if((IS_ATTRIBUTE(attributes, TPMA_NV, PLATFORMCREATE)
        && in->authHandle == TPM_RH_OWNER)
       || (!IS_ATTRIBUTE(attributes, TPMA_NV, PLATFORMCREATE)
           && in->authHandle == TPM_RH_PLATFORM))
        return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_authHandle;

    // If TPMA_NV_POLICY_DELETE is SET, then the index must be defined by
    // the platform
    if(IS_ATTRIBUTE(attributes, TPMA_NV, POLICY_DELETE)
       &&  TPM_RH_PLATFORM != in->authHandle)
        return TPM_RCS_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

    // Make sure that the TPMA_NV_WRITEALL is not set if the index size is larger
    // than the allowed NV buffer size.
    if(in->publicInfo.nvPublic.dataSize > MAX_NV_BUFFER_SIZE
       &&  IS_ATTRIBUTE(attributes, TPMA_NV, WRITEALL))
        return TPM_RCS_SIZE + RC_NV_DefineSpace_publicInfo;

    // And finally, see if the index is already defined.
    if(NvIndexIsDefined(in->publicInfo.nvPublic.nvIndex))
        return TPM_RC_NV_DEFINED;

// Internal Data Update
    // define the space.  A TPM_RC_NV_SPACE error may be returned at this point
    return NvDefineIndex(&in->publicInfo.nvPublic, &in->auth);
}

#endif // CC_NV_DefineSpace